/**
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.template.flow;

import static cc.aileron.template.flow.FlowCategory.*;

import java.util.EnumMap;

import cc.aileron.accessor.PojoAccessor;
import cc.aileron.accessor.PojoAccessorMethod;
import cc.aileron.accessor.PojoAccessorValue;
import cc.aileron.accessor.PojoAccessorValueNotFoundException;
import cc.aileron.accessor.PojoPropertiesNotFoundException;
import cc.aileron.template.context.TemplateContext;

import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * @author Aileron
 */
@Singleton
public class FlowMethodProviderMapImpl implements FlowMethodProviderMap
{
    @Override
    public FlowMethodProvider get(final FlowCategory category)
    {
        return map.get(category);
    }

    /**
     * @param def
     * @param each
     * @param with
     * @param comment
     */
    @Inject
    public FlowMethodProviderMapImpl(
        final FlowDef def,
        final FlowEach each,
        final FlowWith with,
        final FlowComment comment)
    {
        map = new EnumMap<FlowCategory, FlowMethodProvider>(FlowCategory.class);
        map.put(DEF, def);
        map.put(EACH, each);
        map.put(WITH, with);
        map.put(COMMENT, comment);
    }

    private final EnumMap<FlowCategory, FlowMethodProvider> map;
}

class FlowComment implements FlowMethodProvider
{
    @Override
    public Object get(final String args)
    {
        return null;
    }
}

class FlowDef implements FlowMethodProvider
{
    public FlowDefMethod get(final String args)
    {
        return new FlowDefMethod()
        {
            public boolean call(final TemplateContext context) throws PojoAccessorValueNotFoundException,
                    PojoPropertiesNotFoundException
            {
                final String[] token = args.split(" @ ");
                final String key = token[0];
                final String compKey;
                if (token.length >= 2)
                {
                    compKey = token[1];
                }
                else
                {
                    compKey = null;
                }
                final PojoAccessorValue accessor = context.getAccessor()
                    .to(key);
                if (!accessor.exist(PojoAccessorMethod.GET))
                {
                    return false;
                }
                if (!Enum.class.isAssignableFrom(accessor.type()))
                {
                    return boolValue(accessor, compKey);
                }
                return enumValue(accessor, compKey);
            }

            /**
             * @param accessor
             * @param compKey
             * @return bool
             */
            private boolean boolValue(final PojoAccessorValue accessor,
                    final String compKey)
            {
                final Boolean value = accessor.value(Boolean.class);
                if (compKey == null)
                {
                    return value != null ? value.booleanValue() : false;
                }
                final Boolean compValue = Boolean.parseBoolean(compKey);
                return compValue.equals(value);
            }

            /**
             * @param accessor
             * @param compKey
             * @return bool
             */
            private boolean enumValue(final PojoAccessorValue accessor,
                    final String compKey)
            {
                final Enum<?> value = accessor.value(Enum.class);
                if (compKey == null)
                {
                    return value != null;
                }
                return compKey.equals(value.name());
            }
        };
    }
}

class FlowEach implements FlowMethodProvider
{
    public FlowEachMethod get(final String args)
    {
        final String[] token = args.split(" ");
        final String key = token[0];
        final String indexKey;
        if (token.length >= 2)
        {
            indexKey = token[1];
        }
        else
        {
            indexKey = null;
        }

        return new FlowEachMethod()
        {
            public Iterable<PojoAccessor<Object>> call(final TemplateContext context) throws PojoAccessorValueNotFoundException,
                    PojoPropertiesNotFoundException
            {

                return context.getAccessor()
                    .to(key)
                    .accessorIterable(Object.class);
            }

            @Override
            public int startCount(final TemplateContext context) throws PojoAccessorValueNotFoundException,
                    PojoPropertiesNotFoundException
            {
                if (indexKey == null)
                {
                    return 0;
                }
                return context.getAccessor()
                    .to(indexKey)
                    .value(Number.class)
                    .intValue();
            }
        };
    }
}

class FlowWith implements FlowMethodProvider
{
    public FlowWithMethod get(final String args)
    {
        return new FlowWithMethod()
        {
            @Override
            public PojoAccessor<?> call(final TemplateContext context) throws PojoAccessorValueNotFoundException,
                    PojoPropertiesNotFoundException
            {
                return context.getAccessor()
                    .to(args)
                    .accessor(Object.class);
            }
        };
    }
}